package com.deloitte.payrollmgtsys.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.Timestamp;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.deloitte.payrollmgtsys.util.GoogleAuthUtil;

/**
 * Responsible for interacting with Google OAuth Service.
 * When user logs into the system, the typical flow of calls are: 
 * /signIn -> (Google OAuth) -> /getToken -> /getUserInfo
 * 
 * @author muwang
 *
 */
@Controller
public class GoogleUserAuth {

	/**
	 * Raise sign in request to Google OAuth Service to get a code that can be exchanged 
	 * for OAuth access token. This step requires user-interaction on user's first entry
	 * since it redirects user to Google Authentication Page.
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	@RequestMapping("/signIn")
	public void signIn(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		response.sendRedirect("https://accounts.google.com/o/oauth2/auth?"
				+ "scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&"
				+ "state=%2Fprofile&" + "redirect_uri="
				+ GoogleAuthUtil.getRedirectURL(request.getServerName()) + "&"
				+ "response_type=code&" + "client_id="
				+ GoogleAuthUtil.getClientID(request.getServerName())
				+ "&approval_prompt=auto");
	}

	/**
	 * Exchange code for a new Token.
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	@RequestMapping("/getToken")
	public void getToken(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		System.out.println(request.getServerName());
		
		String code = request.getParameter("code");

		if (code == null || code.equals("")) {
			code = (String) request.getSession().getAttribute("code");
		}

		request.getSession().setAttribute("code", code);

		if (code != null && !code.equals("")) {
			getNewToken(request);
			request.getRequestDispatcher("/mvc/getUserInfo").forward(
					request, response);
		}
	}

	/**
	 * Static Method to get a new Token. It can be called from any other place of the project to programingly 
	 * refresh token.
	 * 
	 * @param request
	 * @throws IOException
	 */
	public static void getNewToken(HttpServletRequest request) throws IOException {
		HttpURLConnection connection = null;
		BufferedReader rd = null;
		StringBuilder sb = null;
		String line = null;
		URL serverAddress = null;
		InputStream is = null;
		OutputStream os = null;
		String url = "https://accounts.google.com/o/oauth2/token";
		JSONObject tokenJson = null;
		StringBuffer param = new StringBuffer(300);
		String code = (String) request.getSession().getAttribute("code");
		param.append("code=")
				.append(code)
				.append("&")
				.append("client_id="
						+ GoogleAuthUtil.getClientID(request.getServerName())
						+ "&")
				.append("client_secret="
						+ GoogleAuthUtil.getClientSecret(request
								.getServerName()) + "&")
				.append("redirect_uri="
						+ GoogleAuthUtil.getRedirectURL(request.getServerName())
						+ "&").append("grant_type=authorization_code");
		try {
			serverAddress = new URL(url);
			connection = (HttpURLConnection) serverAddress.openConnection();

			connection.setRequestMethod("POST");
			connection.setDoOutput(true);
			connection.setReadTimeout(10000);

			os = connection.getOutputStream();
			os.write(param.toString().getBytes());

			connection.connect();

			is = connection.getInputStream();
			rd = new BufferedReader(new InputStreamReader(is));
			sb = new StringBuilder();
			while ((line = rd.readLine()) != null) {
				sb.append(line + '\n');
			}

			try {
				tokenJson = new JSONObject(sb.toString());
				String token = tokenJson.getString("access_token");
				request.getSession().setAttribute("access_token", token);
				long validToTimestamp = tokenJson.getInt("expires_in") * 1000
						+ System.currentTimeMillis();
				request.getSession().setAttribute("valid_to", validToTimestamp);
				Timestamp validToDate = new Timestamp(validToTimestamp);
				System.out.println("New token: " + token);
				System.out.println("Valid to: " + validToDate + " Timestamp: "
						+ validToTimestamp);
			} catch (JSONException e) {
				e.printStackTrace();
			}

		} finally {
			if (is != null) {
				is.close();
			}
			if (os != null) {
				os.close();
			}
		}
	}

	/**
	 * Use the token to get information for the logon user from Google.
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	@RequestMapping("/getUserInfo")
	public void getUserInfo(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		HttpURLConnection connection = null;
		BufferedReader rd = null;
		StringBuilder sb = null;
		String line = null;
		URL serverAddress = null;
		InputStream is = null;
		OutputStream os = null;
		String url = "";
		JSONObject userJson = null;

		String accessToken = (String) request.getSession().getAttribute(
				"access_token");
		if (accessToken != null && accessToken != "") {
			url = "https://www.googleapis.com/oauth2/v1/userinfo?access_token="
					+ accessToken;
			try {
				serverAddress = new URL(url);
				connection = (HttpURLConnection) serverAddress.openConnection();

				connection.setRequestMethod("GET");
				connection.setDoOutput(true);
				connection.setReadTimeout(10000);

				connection.connect();

				is = connection.getInputStream();
				rd = new BufferedReader(new InputStreamReader(is));
				sb = new StringBuilder();
				while ((line = rd.readLine()) != null) {
					sb.append(line + '\n');
				}
				try {
					userJson = new JSONObject(sb.toString());
					request.getSession().setAttribute("userEmail",
							userJson.getString("email"));
					request.getSession().setAttribute("userFirstname",
							userJson.getString("given_name"));
					request.getSession().setAttribute("userSurname",
							userJson.getString("family_name"));
					Object origURL = request.getSession().getAttribute(
							"orig_url");
					if (origURL != null && !origURL.equals("")) {
						response.sendRedirect((String) origURL);
					}
				} catch (JSONException e) {
					e.printStackTrace();
				}

			} finally {
				if (is != null) {
					is.close();
				}
				if (os != null) {
					os.close();
				}
			}
		}
	}
}
